package cn.elane.elastic.core.service.biz;

import cn.elane.elastic.global.model.NameBean;
import cn.elane.elastic.global.model.NameBeanUtil;
import cn.elane.elastic.global.render.R;
import cn.elane.elastic.global.util.HttpUtil;
import cn.elane.elastic.global.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalField;
import java.util.ArrayList;
import java.util.List;

@Component
public class ServiceBiz {

	@Value("${application.url}")
	private String url;

	@Value("${application.kbnVersion}")
	private String ver;

	private String utf8 = "utf-8";
	private String start = "start";
	private String end = "end";

	private R checkParam(NameBean param) {
		if (param == null || param.isEmpty()) {
			return R.fail("参数不能为空!");
		}
		if (!(!param.isEmpty(start) && param.str(start).length() == 19 && !param.isEmpty(end) && param.str(end).length() == 19)) {
			return R.fail("\u8d77\u59cb\u65f6\u95f4\u683c\u5f0f\u9519\u8bef!");
		}
		return null;
	}

	public R getEnv(String start, String end) {
		if (StringUtil.isEmpty(start) || StringUtil.isEmpty(end)) {
			return R.fail("参数不能为空!");
		}
		if (start.length() != 19 || end.length() != 19) {
			return R.fail("参数格式错误!");
		}
		try {
			start = URLEncoder.encode(start.replace(" ", "T") + ".000Z", utf8);
			end = URLEncoder.encode(end.replace(" ", "T") + ".000Z", utf8);
			String s = HttpUtil.get(
					String.format("%s/api/apm/ui_filters/environments?start=%s&end=%s", url, start, end));
			JSONArray rs = new JSONArray();
			rs.add("ENVIRONMENT_ALL");
			rs.addAll(JSONArray.parseArray(s));
			return R.success(rs);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	public R getParams(String start, String end, String environment) {
		try {
			if (start.length() != 19 || end.length() != 19) {
				return R.fail("起始时间格式错误!");
			}
			String filterNames = "[\"host\",\"agentName\",\"transactionResult\"]";
			start = URLEncoder.encode(start.replace(" ", "T") + ".000Z", utf8);
			end = URLEncoder.encode(end.replace(" ", "T") + ".000Z", utf8);
			NameBean filter = new NameBean();
			filter.put("environment", environment);
			String s = HttpUtil.get(
					String.format("%s/api/apm/ui_filters/local_filters/services?start=%s&end=%s&uiFilters=%s&filterNames=%s"
							, url
							, start
							, end
							, URLEncoder.encode(filter.toJson(), utf8)
							, URLEncoder.encode(filterNames, utf8))
			);
			List<NameBean> list = NameBeanUtil.init2List(s);
			NameBean rs = new NameBean();
			if (list != null && list.size() > 0) {
				for (NameBean l : list) {
					rs.put(l.str("name"), l.beans("options"));
				}
			}
			return R.success(rs);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	public R list(NameBean param) {
		R r = checkParam(param);
		if (r != null) {
			return r;
		}
		try {
			param.put(start, URLEncoder.encode(param.str(start).replace(" ", "T") + ".000Z", utf8));
			param.put(end, URLEncoder.encode(param.str(end).replace(" ", "T") + ".000Z", utf8));
			NameBean filter = new NameBean();
			filter.put("environment", param.str("environment"));

			if (!param.isEmpty("host")) {
				JSONArray host = new JSONArray();
				host.add(param.str("host"));
				filter.put("host", host);
			}
			if (!param.isEmpty("agentName")) {
				JSONArray agentName = new JSONArray();
				agentName.add(param.str("agentName"));
				filter.put("agentName", agentName);
			}

			if (!param.isEmpty("serviceName")) {
				filter.put("kuery", "service.name : " + param.str("serviceName"));
			}

			String s = HttpUtil.get(
					String.format("%s/api/apm/services?start=%s&end=%s&uiFilters=%s"
							, url, param.str(start)
							, param.str(end)
							, URLEncoder.encode(filter.toJson(), utf8))
			);
			List<NameBean> list = new NameBean(s).beans("items");
			return R.success(list);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	public R getTraceParams(String start, String end, String environment) {
		try {
			if (start.length() != 19 || end.length() != 19) {
				return R.fail("起始时间格式错误!");
			}
			String filterNames = "[\"host\",\"transactionResult\"]";
			start = URLEncoder.encode(start.replace(" ", "T") + ".000Z", utf8);
			end = URLEncoder.encode(end.replace(" ", "T") + ".000Z", utf8);
			NameBean filter = new NameBean();
			filter.put("environment", environment);

			String s = HttpUtil.get(
					String.format("%s/api/apm/ui_filters/local_filters/traces?start=%s&end=%s&uiFilters=%s&filterNames=%s"
							, url
							, start
							, end
							, URLEncoder.encode(filter.toJson(), utf8)
							, URLEncoder.encode(filterNames, utf8))
			);
			List<NameBean> list = NameBeanUtil.init2List(s);
			NameBean rs = new NameBean();
			if (list != null && list.size() > 0) {
				for (NameBean l : list) {
					rs.put(l.str("name"), l.beans("options"));
				}
			}
			return R.success(rs);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

//	public R traceList(NameBean param) {
//		if (param == null || param.isEmpty()) {
//			return R.fail("参数不能为空!");
//		}
//		if (param.isEmpty(start) || param.str(start).length() != 19 || param.isEmpty(end) || param.str(end).length() != 19) {
//			return R.fail("起始时间格式错误!");
//		}
//		try {
//			R names = getServiceNames();
//			if (names.getSuccess() && names.getData() != null) {
//				Object data = names.getData();
//				JSONArray obj = JSONArray.parseArray(data.toString());
//				for (int i = 0; i < obj.size(); i++) {
//					System.err.println(obj.get(i));
//				}
//			}
//
//			return R.success();
//		} catch (Exception e) {
//			e.printStackTrace();
//			return R.fail("异常!");
//		}
//	}

	//原接口列表
	public R traceList(NameBean param) {
		R r = checkParam(param);
		if (r != null) {
			return r;
		}
		try {
			param.put(start, URLEncoder.encode(param.str(start).replace(" ", "T") + ".000Z", utf8));
			param.put(end, URLEncoder.encode(param.str(end).replace(" ", "T") + ".000Z", utf8));
			NameBean filter = new NameBean();
			filter.put("environment", param.str("environment"));

			if (!param.isEmpty("host")) {
				JSONArray host = new JSONArray();
				host.add(param.str("host"));
				filter.put("host", host);
			}
			if (!param.isEmpty("result")) {
				JSONArray result = new JSONArray();
				result.add(param.str("result"));
				filter.put("transactionResult", result);
			}

			if (!param.isEmpty("serviceName")) {
				filter.put("kuery", "service.name : " + param.str("serviceName"));
			}

			String s = HttpUtil.get(
					String.format("%s/api/apm/traces?start=%s&end=%s&uiFilters=%s"
							, url
							, param.str(start)
							, param.str(end)
							, URLEncoder.encode(filter.toJson(), utf8))
			);
			List<NameBean> list = new NameBean(s).beans("items");
			if (list != null && !list.isEmpty()) {
				BigDecimal divide = new BigDecimal(1000);
//				List<NameBean> all = new ArrayList<>();
				for (NameBean l : list) {
					BigDecimal impact = new BigDecimal(l.str("impact"));
					String str = "";
					if (impact.doubleValue() < 1) {
						str = "0";
					}
					str += new DecimalFormat("#.00").format(impact);
					l.put("impact", str + "%");
					str = "";
					BigDecimal art = new BigDecimal(l.str("averageResponseTime")).divide(divide);
					if (art.doubleValue() < 1) {
						str = "0";
					}
					str += new DecimalFormat("#.0").format(art);
					l.put("averageResponseTime", str + " ms");

					BigDecimal tpm = new BigDecimal(l.str("transactionsPerMinute"));
					int i = tpm.compareTo(BigDecimal.valueOf(0.1));
					if (i < 0) {
						l.put("transactionsPerMinute", "< 0.1 tpm");
					} else if (i == 0) {
						l.put("transactionsPerMinute", "= 0.1 tpm");
					} else {
						l.put("transactionsPerMinute", tpm.toString());
					}

					//TODO:获取链路内接口列表
					/*String rs = HttpUtil.get(url + "/api/apm/services/"
							+ l.str("serviceName")
							+ "/transactions/charts/distribution?start=" + param.str(start)
							+ "&end=" + param.str(end)
							+ "&uiFilters=" + URLEncoder.encode(filter.toJson(), utf8)
							+ "&transactionType=" + l.str("transactionType")
							+ "&transactionName=" + URLEncoder.encode(l.str("transactionName"), utf8));
					System.err.println(rs);*/
//					all.addAll(new NameBean(rs).beans("buckets"));
					/*List<NameBean> buckets = new NameBean(rs).beans("buckets");
					System.err.println(NameBeanUtil.toJson(buckets));
					for (NameBean b : buckets) {
						if (b.getInteger("count") > 0) {
							List<NameBean> samples = b.beans("samples");
							for (NameBean sample : samples) {

								String ss = HttpUtil.get(url + "/api/apm/traces/" + sample.str("traceId") + "?"
										+ "start=" + param.str(start)
										+ "&end=" + param.str(end));
								System.err.println(ss);
							}
						}
					}*/
				}
			}
			return R.success(list);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	public R traceListById(NameBean param) {
		R r = checkParam(param);
		if (r != null) {
			return r;
		}
		try {
			param.put(start, URLEncoder.encode(param.str(start).replace(" ", "T") + ".000Z", utf8));
			param.put(end, URLEncoder.encode(param.str(end).replace(" ", "T") + ".000Z", utf8));
			NameBean filter = new NameBean();
			filter.put("environment", param.str("environment"));

			if (!param.isEmpty("host")) {
				JSONArray host = new JSONArray();
				host.add(param.str("host"));
				filter.put("host", host);
			}
			if (!param.isEmpty("result")) {
				JSONArray result = new JSONArray();
				result.add(param.str("result"));
				filter.put("transactionResult", result);
			}

			if (!param.isEmpty("serviceName")) {
				filter.put("kuery", "service.name : " + param.str("serviceName"));
			}

			String rs = HttpUtil.get(
					String.format("%s/api/apm/services/%s/transactions/charts/distribution?start=%s&end=%s&uiFilters=%s&transactionType=%s&transactionName=%s"
							, url
							, param.str("serviceName")
							, param.str(start)
							, param.str(end)
							, URLEncoder.encode(filter.toJson(), utf8)
							, param.str("transactionType")
							, URLEncoder.encode(param.str("transactionName"), utf8))
			);
			/*List<NameBean> all = new ArrayList<>();
			List<NameBean> buckets = new NameBean(rs).beans("buckets");
			all.addAll(buckets);
			System.err.println(NameBeanUtil.toJson(buckets));
			for (NameBean b : buckets) {
				if (b.getInteger("count") > 0) {
					List<NameBean> samples = b.beans("samples");
					for (NameBean sample : samples) {
						String ss = HttpUtil.get(url + "/api/apm/traces/" + sample.str("traceId") + "?"
								+ "start=" + param.str(start)
								+ "&end=" + param.str(end));
						System.err.println(ss);
					}
				}
			}

			System.err.println("=========================================================================================");
			String s = HttpUtil.post(
					String.format("%s/api/kibana/suggestions/values/apm-*", url)
					, "{\"query\":\"\",\"field\":\"service.name\",\"filters\":[{\"term\":{\"processor.event\":\"transaction\"}}]}");
			System.err.println(s);
			System.err.println(new NameBean(s));*/

			return R.success(new NameBean(rs).beans("buckets"));
		} catch (Exception e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	public R getServiceNames() {
		String addr = "/api/kibana/suggestions/values/apm-*";
		String bodyStr = "{\"query\":\"\",\"field\":\"service.name\",\"filters\":[]}";
		NameBean header = new NameBean() {{
			put("kbn-version", ver);
		}};
		NameBean body = new NameBean(bodyStr);

		String s = HttpUtil.post2(url + addr, header, body);
		return R.success(JSONArray.parseArray(s));
	}

	public R serviceMap(NameBean param) {
		R r = checkParam(param);
		if (r != null) {
			return r;
		}
		try {
			param.put(start, URLEncoder.encode(param.str(start).replace(" ", "T") + ".000Z", utf8));
			param.put(end, URLEncoder.encode(param.str(end).replace(" ", "T") + ".000Z", utf8));
			if (param.str("environment").equals("ENVIRONMENT_ALL")) {
				param.put("environment", "");
			}

			String s = HttpUtil.get(
					String.format("%s/api/apm/service-map?start=%s&end=%s&environment=%s&serviceName=%s"
							, url
							, param.str(start)
							, param.str(end)
							, param.str("environment")
							, param.str("serviceName")
					)
			);
			if (StringUtil.isEmpty(s)) {
				return R.fail("接口异常!");
			}
			List<NameBean> list = new NameBean(s).beans("elements");
			List<NameBean> nodes = new ArrayList<>();
			List<NameBean> edges = new ArrayList<>();
			if (list != null && list.size() > 0) {
				for (NameBean l : list) {
					l.append(l.bean("data")).remove("data");
					if (l.isEmpty("sourceData") && l.isEmpty("targetData")) {
						if (l.isEmpty("label")) {
							l.put("label", l.str("service.name"));
						}
						if (l.isEmpty("span")) {
							l.put("span.type", "server");
							l.put("span.subtype", "java");
						}
						nodes.add(l);
					} else {
						edges.add(l);
					}
				}
			}

			NameBean rs = new NameBean();
			NameBean total = new NameBean();

			NameBean filter = new NameBean();
			JSONArray result = new JSONArray();
			filter.put("environment", param.str("environment"));
			if (!param.isEmpty("serviceName")) {
				filter.put("kuery", "service.name : \"" + param.str("serviceName") + "\"");
			}
			result.add("HTTP 3xx");
			filter.put("transactionResult", result);
			String e3 = HttpUtil.get(
					String.format("%s/api/apm/traces?start=%s&end=%s&uiFilters=%s"
							, url
							, param.str(start)
							, param.str(end)
							, URLEncoder.encode(filter.toJson(), utf8))
			);

			List<NameBean> e3List = new NameBean(e3).beans("items");
			total.put("HTTP 3xx", new NameBean() {{
				put("total", (e3List == null ? 0 : e3List.size()));
				put("data", e3List);
			}});

			result.clear();
			result.add("HTTP 4xx");
			filter.put("transactionResult", result);
			String e4 = HttpUtil.get(
					String.format("%s/api/apm/traces?start=%s&end=%s&uiFilters=%s"
							, url
							, param.str(start)
							, param.str(end)
							, URLEncoder.encode(filter.toJson(), utf8))
			);

			List<NameBean> e4List = new NameBean(e4).beans("items");
			total.put("HTTP 4xx", new NameBean() {{
				put("total", (e4List == null ? 0 : e4List.size()));
				put("data", e4List);
			}});

			result.clear();
			result.add("HTTP 5xx");
			filter.put("transactionResult", result);
			String e5 = HttpUtil.get(
					String.format("%s/api/apm/traces?start=%s&end=%s&uiFilters=%s"
							, url
							, param.str(start)
							, param.str(end)
							, URLEncoder.encode(filter.toJson(), utf8))
			);

			List<NameBean> e5List = new NameBean(e5).beans("items");
			total.put("HTTP 5xx", new NameBean() {{
				put("total", (e5List == null ? 0 : e5List.size()));
				put("data", e5List);
			}});

			rs.put("data", new NameBean().put("nodes", nodes).put("edges", edges));
			total.put("serviceCount", nodes.size());
			rs.put("total", total);
			return R.success(rs);
		} catch (Exception e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	public R mapDetail(NameBean param) {
		R r = checkParam(param);
		if (r != null) {
			return r;
		}
		if (param.isEmpty("serviceName")) {
			return R.fail("应用名称不能为空!");
		}
		try {
			param.put(start, URLEncoder.encode(param.str(start).replace(" ", "T") + ".000Z", utf8));
			param.put(end, URLEncoder.encode(param.str(end).replace(" ", "T") + ".000Z", utf8));
			NameBean filter = new NameBean();
			filter.put("kuery", "");
			filter.put("environment", param.str("environment"));

			String s = HttpUtil.get(
					String.format("%s/api/apm/service-map/service/%s?start=%s&end=%s&uiFilters=%s"
							, url
							, param.str("serviceName")
							, param.str(start)
							, param.str(end)
							, URLEncoder.encode(filter.toJson(), utf8)
					)
			);
			if (StringUtil.isEmpty(s)) {
				return R.fail("接口异常!");
			}
			return R.success(new NameBean(s));
		} catch (Exception e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	private String pre(NameBean param) {
		String str = "{\"request\":{\"id\":\"\",\"params\":{\"index\":\"apm-*\",\"body\":{\"version\":true,\"size\":$size$,\"sort\":[{\"@timestamp\":{\"order\":\"desc\",\"unmapped_type\":\"boolean\"}}],\"aggs\":{\"2\":{\"date_histogram\":{\"field\":\"@timestamp\",\"fixed_interval\":\"12h\",\"min_doc_count\":1,\"time_zone\":\"Asia/Shanghai\"}}},\"fields\":[{\"field\":\"@timestamp\",\"format\":\"date_time\"},{\"field\":\"event.created\",\"format\":\"date_time\"},{\"field\":\"event.end\",\"format\":\"date_time\"},{\"field\":\"event.ingested\",\"format\":\"date_time\"},{\"field\":\"event.start\",\"format\":\"date_time\"},{\"field\":\"file.accessed\",\"format\":\"date_time\"},{\"field\":\"file.created\",\"format\":\"date_time\"},{\"field\":\"file.ctime\",\"format\":\"date_time\"},{\"field\":\"file.mtime\",\"format\":\"date_time\"},{\"field\":\"file.x509.not_after\",\"format\":\"date_time\"},{\"field\":\"file.x509.not_before\",\"format\":\"date_time\"},{\"field\":\"package.installed\",\"format\":\"date_time\"},{\"field\":\"process.parent.start\",\"format\":\"date_time\"},{\"field\":\"process.start\",\"format\":\"date_time\"},{\"field\":\"tls.client.not_after\",\"format\":\"date_time\"},{\"field\":\"tls.client.not_before\",\"format\":\"date_time\"},{\"field\":\"tls.client.x509.not_after\",\"format\":\"date_time\"},{\"field\":\"tls.client.x509.not_before\",\"format\":\"date_time\"},{\"field\":\"tls.server.not_after\",\"format\":\"date_time\"},{\"field\":\"tls.server.not_before\",\"format\":\"date_time\"},{\"field\":\"tls.server.x509.not_after\",\"format\":\"date_time\"},{\"field\":\"tls.server.x509.not_before\",\"format\":\"date_time\"},{\"field\":\"x509.not_after\",\"format\":\"date_time\"},{\"field\":\"x509.not_before\",\"format\":\"date_time\"}],\"script_fields\":{},\"stored_fields\":[\"*\"],\"_source\":{\"excludes\":[\"sourcemap.sourcemap\"]},\"query\":{\"bool\":{\"must\":[],\"filter\":[{\"match_all\":{}},{\"range\":{\"@timestamp\":{\"gte\":\"$start$\",\"lte\":\"$end$\",\"format\":\"strict_date_optional_time\"}}},{\"exists\":{\"field\":\"url.path\"}},{\"exists\":{\"field\":\"service.name\"}}],\"should\":[],\"must_not\":[]}},\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"fragment_size\":2147483647}},\"preference\":$preference$}},\"options\":{\"sessionId\":\"\",\"strategy\":\"ese\",\"isStored\":false,\"isRestore\":false}}";
		str = str.replace("$size$", param.str("size"));
		str = str.replace("$preference$", LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() + "");
		str = str.replace("$start$", param.str(start));
		str = str.replace("$end$", param.str(end));

		JSONObject p = JSON.parseObject(str);
		List<JSONObject> batch = new ArrayList<>();
		batch.add(p);
		JSONObject para = new JSONObject();
		para.put("batch", batch);

		NameBean header = new NameBean() {{
			put("kbn-version", ver);
		}};

		String addr = "/internal/bsearch";
		String s = HttpUtil.post2(url + addr, header, para);

		return new NameBean(s).str("result.id");
	}

	public R search(NameBean param) {
		R r = checkParam(param);
		if (r != null) {
			return r;
		}

		if (param.isEmpty("size")) {
			param.put("size", 500);
		}

		try {
			param.put(start, param.str(start).replace(" ", "T") + ".000Z");
			param.put(end, param.str(end).replace(" ", "T") + ".000Z");

			String str;
			if (param.isEmpty("serviceName")
					&& param.isEmpty("environment")
					&& param.isEmpty("result")
					&& param.isEmpty("urlPath")
					&& param.isEmpty("traceId")) {
				String requestId = pre(param);
				str = "{\"request\":{\"id\":\"" + requestId + "\",\"params\":{\"index\":\"apm-*\",\"body\":{\"version\":true,\"size\":$size$,\"sort\":[{\"@timestamp\":{\"order\":\"desc\",\"unmapped_type\":\"boolean\"}}],\"aggs\":{\"2\":{\"date_histogram\":{\"field\":\"@timestamp\",\"fixed_interval\":\"12h\",\"min_doc_count\":1,\"time_zone\":\"Asia/Shanghai\"}}},\"fields\":[],\"script_fields\":{},\"stored_fields\":[\"*\"],\"_source\":{\"excludes\":[\"sourcemap.sourcemap\"]},\"query\":{\"bool\":{\"must\":[],\"filter\":[{\"match_all\":{}},{\"range\":{\"@timestamp\":{\"gte\":\"$start$\",\"lte\":\"$end$\",\"format\":\"strict_date_optional_time\"}}},{\"exists\":{\"field\":\"url.path\"}},{\"exists\":{\"field\":\"service.name\"}}],\"should\":[],\"must_not\":[]}},\"highlight\":{}},\"preference\":$preference$}},\"options\":{}}";
			} else {
				str = "{\"options\":{},\"request\":{\"params\":{\"body\":{\"_source\":{\"excludes\":[\"sourcemap.sourcemap\"]},\"aggs\":{\"2\":{\"date_histogram\":{\"field\":\"@timestamp\",\"fixed_interval\":\"12h\",\"min_doc_count\":1,\"time_zone\":\"Asia/Shanghai\"}}},\"fields\":[],\"highlight\":{\"fields\":{\"*\":{}},\"fragment_size\":2147483647,\"post_tags\":[\"\"],\"pre_tags\":[\"\"]},\"query\":{\"bool\":{\"filter\":[{\"bool\":{\"filter\":[$environment${\"bool\":{\"filter\":[{\"bool\":{\"minimum_should_match\":1,\"should\":[$transactionResult$]}},{\"bool\":{\"filter\":[{\"bool\":{\"minimum_should_match\":1,\"should\":[$serviceName$]}},{\"bool\":{\"minimum_should_match\":1,\"should\":[$traceId$]}},{\"bool\": {\"minimum_should_match\": 1,\"should\": [$urlPath$]}}]}}]}}]}},{\"exists\":{\"field\":\"url.path\"}},{\"range\":{\"@timestamp\":{\"format\":\"strict_date_optional_time\",\"gte\":\"$start$\",\"lte\":\"$end$\"}}}],\"must\":[],\"must_not\":[],\"should\":[]}},\"script_fields\":{},\"size\":$size$,\"sort\":[{\"@timestamp\":{\"order\":\"desc\",\"unmapped_type\":\"boolean\"}}],\"stored_fields\":[\"*\"],\"version\":true},\"index\":\"apm-*\",\"preference\":$preference$}}}";
			}
			str = str.replace("$size$", param.str("size"));
			str = str.replace("$preference$", LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() + "");

			String serviceName;
			if (param.isEmpty("serviceName")) {
				serviceName = "{\"exists\":{\"field\":\"service.name\"}}";
			} else {
				serviceName = "{\"match_phrase\":{\"service.name\":\"" + param.str("serviceName") + "\"}}";
			}
			str = str.replace("$serviceName$", serviceName);

			String traceId;
			if (param.isEmpty("traceId")) {
				traceId = "{\"exists\":{\"field\":\"trace.id\"}}";
			} else {
				traceId = "{\"match_phrase\":{\"trace.id\":\"" + param.str("traceId") + "\"}}";
			}
			str = str.replace("$traceId$", traceId);

			String result;
			if (param.isEmpty("result")) {
//				transactionResult = "{\"exists\":{\"field\":\"transaction.result\"}}";
				result = "{\"exists\":{\"field\":\"transaction.result\"}}";
			} else {
				result = "{\"match_phrase\":{\"transaction.result\":\"" + param.str("result") + "\"}}";
			}
			str = str.replace("$transactionResult$", result);

			String environment;
			if (param.isEmpty("environment") || "ENVIRONMENT_NOT_DEFINED".equals(param.str("environment"))) {
				environment = "";
			} else if (param.str("environment").equals("ENVIRONMENT_ALL") ||
					param.str("environment").equals("*")) {
//				environment = "{\"exists\":{\"field\":\"service.environment\"}}";
				environment = "{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"exists\":{\"field\":\"service.environment\"}}]}},";
			} else {
				environment = "{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"service.environment\":\"" + param.str("environment") + "\"}}]}},";
//				environment = "{\"match_phrase\":{\"service.environment\":\"" + param.str("environment") + "\"}}";
			}
			str = str.replace("$environment$", environment);

			String urlPath;
			if (param.isEmpty("urlPath")) {
				urlPath = "{\"exists\":{\"field\":\"url.path\"}}";
			} else {
//				urlPath = "{\"match_phrase\":{\"url.path\":\"" + param.str("urlPath") + "\"}}";
				urlPath = "{\"query_string\": {\"fields\": [\"url.path\"],\"query\": \"*" + param.str("urlPath") + "*\"}}";
			}
			str = str.replace("$urlPath$", urlPath);

			str = str.replace("$start$", param.str(start));
			str = str.replace("$end$", param.str(end));

			JSONObject p = JSON.parseObject(str);
			List<JSONObject> batch = new ArrayList<>();
			batch.add(p);
			JSONObject para = new JSONObject();
			para.put("batch", batch);

			NameBean header = new NameBean() {{
				put("kbn-version", ver);
			}};

			String addr = "/internal/bsearch";
			String s = HttpUtil.post3(url + addr, header, para);
			List<NameBean> list = new NameBean(s).beans("result.rawResponse.hits.hits");
			List<NameBean> rss = new ArrayList<>();
			if (list != null && !list.isEmpty()) {
				for (NameBean l : list) {
					NameBean item = new NameBean() {{
						put("sourceIp", l.str("_source.source.ip"));
						put("urlPath", l.str("_source.url.path"));
						put("urlFull", l.str("_source.url.full"));
						put("traceId", l.str("_source.trace.id"));
						put("environment", l.str("_source.service.environment"));
						put("serviceName", l.str("_source.service.name"));
						put("requestMethod", l.str("_source.http.request.method"));
						put("responseStatusCode", l.str("_source.http.response.status_code"));
						LocalDateTime time = LocalDateTime.ofEpochSecond(new BigDecimal(l.str("_source.timestamp.us")).divide(new BigDecimal(1000000)).longValue(), 0, ZoneOffset.ofHours(8));
						String format = time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
						put("time", format);
						put("transactionTime", new BigDecimal(l.getString("_source.transaction.duration.us", "0")).divide(new BigDecimal(1000)).intValue() + " ms");
						put("transactionResult", l.str("_source.transaction.result"));
						put("transactionName", l.str("_source.transaction.name"));
					}};
					rss.add(item);
				}
			}
			return R.success(rss);
		} catch (Exception e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}

	public R searchById(NameBean param) {
		if (param.isEmpty(start) || param.isEmpty(end)) {
			return R.fail("起始时间不能为空!");
		}

		if (param.isEmpty("traceId")) {
			return R.fail("traceId不能为空!");
		}
		try {
			param.put(start, param.str(start).replace(" ", "T") + ".000Z");
			param.put(end, param.str(end).replace(" ", "T") + ".000Z");

			NameBean rs = new NameBean();
			String ss = HttpUtil.get(url + "/api/apm/traces/" + param.str("traceId") + "?"
					+ "start=" + param.str(start)
					+ "&end=" + param.str(end));
			NameBean trace = new NameBean(ss).bean("trace");
			List<NameBean> items = trace.beans("items");
			List<NameBean> errorDocs = trace.beans("errorDocs");

			List<NameBean> traceList = new ArrayList<>();
			List<String> services = new ArrayList<>();
			if (items != null && !items.isEmpty()) {
				for (NameBean i : items) {
					if (!services.contains(i.str("service.name"))) {
						services.add(i.str("service.name"));
					}
					NameBean ni = new NameBean();
					if (traceList.isEmpty()) {
						ni.put("id", i.str("transaction.id"));
						ni.put("name", i.str("transaction.name"));
						BigDecimal tdu = new BigDecimal(i.str("transaction.duration.us"));
						ni.put("tdu", (tdu.divide(new BigDecimal(1000)).doubleValue()) >= 1 ? tdu.divide(new BigDecimal(1000)).intValue() + " ms" : tdu.intValue() + " μs");
						ni.put("pId", "");
						ni.put("spanType", i.str("transaction.type"));
						ni.put("urlFull", i.str("url.full"));
						ni.put("requestMethod", i.str("http.request.method"));

						if (errorDocs != null && !errorDocs.isEmpty()) {
							List<NameBean> errors = new ArrayList<>();
							for (NameBean errorDoc : errorDocs) {
								if (errorDoc.str("transaction.id").equals(ni.str("id"))) {
									errors.add(errorDoc);
								}
							}
							if (!errors.isEmpty()) {
								ni.put("errorDoc", errors);
							}
						}
						ni.put("detail", i);
						traceList.add(ni);
						continue;
					}
					if (i.isEmpty("span")) {
						ni.put("id", i.str("transaction.id"));
						ni.put("name", i.str("transaction.name"));
						BigDecimal tdu = new BigDecimal(i.str("transaction.duration.us"));
						ni.put("tdu", (tdu.divide(new BigDecimal(1000)).doubleValue()) >= 1 ? tdu.divide(new BigDecimal(1000)).intValue() + " ms" : tdu.intValue() + " μs");
						ni.put("pId", i.str("parent.id"));
						ni.put("spanType", i.str("transaction.type"));
					} else {
						ni.put("id", i.str("span.id"));
						ni.put("name", i.str("span.name"));
						BigDecimal tdu = new BigDecimal(i.str("span.duration.us"));
						ni.put("tdu", (tdu.divide(new BigDecimal(1000)).doubleValue()) >= 1 ? tdu.divide(new BigDecimal(1000)).intValue() + " ms" : tdu.intValue() + " μs");
						ni.put("pId", i.str("parent.id"));
						ni.put("spanType", i.str("span.type"));
						ni.put("subType", i.str("transaction.subtype"));
						if (i.str("span.type").equals("db")) {
							ni.put("db", i.bean("span.db"));
							ni.put("stacktrace", i.beans("span.stacktrace"));
						}
					}
					if (errorDocs != null && !errorDocs.isEmpty()) {
						List<NameBean> errors = new ArrayList<>();
						for (NameBean errorDoc : errorDocs) {
							if (errorDoc.str("transaction.id").equals(i.str("transaction.id"))) {
								errors.add(errorDoc);
							}
						}
						if (!errors.isEmpty()) {
							ni.put("errorDoc", errors);
						}
					}
					ni.put("detail", i);
					traceList.add(ni);
				}
			}
			rs.put("services", services);
			if (!traceList.isEmpty()) {
				List<NameBean> nameBeans = NameBeanUtil.toTreeList(traceList, "id", "pId", "");
				rs.put("trace", new NameBean(ss).bean("trace").put("items", nameBeans.get(0)));
			} else {
				rs.put("trace", new NameBean(ss).bean("trace").put("items", new NameBean()));
			}
			return R.success(rs);
		} catch (Exception e) {
			e.printStackTrace();
			return R.fail("异常!");
		}
	}
}